Skip to content

feat: add session details tooltip to course detail calendar#955

Open
abdulateeb wants to merge 2 commits intoalphaonelabs:mainfrom
abdulateeb:fix/session-calendar-tooltip
Open

feat: add session details tooltip to course detail calendar#955
abdulateeb wants to merge 2 commits intoalphaonelabs:mainfrom
abdulateeb:fix/session-calendar-tooltip

Conversation

@abdulateeb
Copy link
Contributor

@abdulateeb abdulateeb commented Feb 25, 2026

Related issues

Fixes #954

Checklist

  • Did you run the pre-commit? (If not, your PR will most likely not pass — please ensure it passes pre-commit)
  • Did you test the change? (Ensure you didn’t just prompt the AI and blindly commit — test the code and confirm it works)
  • Added screenshots to the PR description (if applicable)

Problem

The session calendar on the course detail page highlights dates with sessions (teal text + dot indicator), but hovering over those dates provides no information about which session is scheduled. Users must scroll to the sessions list to find out.

Solution

Added hover tooltips to calendar dates showing session title and time range.

Screenshots

Before:

image

After:

image

Summary by CodeRabbit

  • New Features
    • Course calendar now shows hoverable tooltips for days with sessions, listing session titles and start/end times; day labels become clickable when sessions exist.
  • Improvements
    • Calendar container allows content to overflow for proper tooltip rendering; today highlighting and session indicators preserved.
  • Tests
    • Added/updated tests to verify per-day tooltip-ready session data in rendered HTML and AJAX calendar responses.

@github-actions github-actions bot added the files-changed: 3 PR changes 3 files label Feb 25, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Walkthrough

Adds per-day session tooltips to the course calendar: backend calendar payloads now include per-date session lists (title, start_time, end_time); template/JS render hoverable tooltips and ARIA attributes; tests updated to assert tooltip-ready session data in HTML and AJAX responses.

Changes

Cohort / File(s) Summary
Template & UI
web/templates/courses/detail.html
Render day cells as hoverable groups, switch calendar container overflow to visible, add per-day tooltip markup (tooltip-) showing session title and time range, include ARIA attributes and teal session indicator.
Backend: calendar data
web/views.py
Replace session date set with month_sessions and session_map; each calendar cell now includes sessions (list of {title,start_time,end_time}), has_session, and is_today for both template and AJAX calendar payloads.
Tests
web/tests/test_views.py
Expand test_calendar_display to validate per-day session entries (title, start_time, end_time) in rendered calendar; add test_calendar_ajax_endpoint to assert AJAX response includes calendar_weeks, current_month, and per-day sessions with correct fields and values.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant TemplateJS as "Template JS"
    participant Server
    participant DB
    Browser->>TemplateJS: Load course detail page
    TemplateJS->>Server: Request calendar data (initial/AJAX)
    Server->>DB: Query sessions for course (year/month)
    DB-->>Server: Session rows
    Server-->>TemplateJS: Calendar payload (weeks with per-day sessions)
    TemplateJS-->>Browser: Render calendar days + attach tooltip HTML on days with sessions
    Browser-->>Browser: Hover day -> show tooltip (session title + time)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

d4

Suggested reviewers

  • A1L13N
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding session tooltips to the course detail calendar.
Linked Issues check ✅ Passed All requirements from issue #954 are met: tooltips display session title and time on hover, tooltip markup is added with ARIA attributes for accessibility, and session data is properly integrated into calendar rendering.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the tooltip feature from issue #954; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/views.py (1)

3384-3397: 🧹 Nitpick | 🔵 Trivial

Avoid rescanning month sessions for each day cell.

Line 3384 currently filters month_sessions per calendar day, which is avoidable work. Pre-group once by date (as done in course_detail) and reuse.

Refactor sketch
-    # Get sessions for the current month
-    month_sessions = course.sessions.filter(start_time__year=year, start_time__month=month).order_by("start_time")
+    # Get sessions for the current month and group by date
+    month_sessions = course.sessions.filter(start_time__year=year, start_time__month=month).order_by("start_time")
+    session_map = {}
+    for s in month_sessions:
+        day_key = s.start_time.date()
+        if day_key not in session_map:
+            session_map[day_key] = []
+        session_map[day_key].append(
+            {
+                "title": s.title,
+                "start_time": s.start_time.strftime("%I:%M %p"),
+                "end_time": s.end_time.strftime("%I:%M %p"),
+            }
+        )

@@
-                sessions_on_day = [s for s in month_sessions if s.start_time.date() == date]
+                sessions_on_day = session_map.get(date, [])
                 calendar_week.append(
                     {
                         "date": date.isoformat() if date else None,
                         "has_session": bool(sessions_on_day),
                         "is_today": date == today,
-                        "sessions": [
-                            {
-                                "title": s.title,
-                                "start_time": s.start_time.strftime("%I:%M %p"),
-                                "end_time": s.end_time.strftime("%I:%M %p"),
-                            }
-                            for s in sessions_on_day
-                        ],
+                        "sessions": sessions_on_day,
                     }
                 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` around lines 3384 - 3397, The code repeatedly filters
month_sessions for each day cell (sessions_on_day) causing unnecessary rescans;
instead, pre-group month_sessions once into a dict keyed by date (as done in
course_detail) — e.g., build a mapping like sessions_by_date =
{s.start_time.date(): [s, ...] for s in month_sessions} before the calendar
loop, then replace the per-day filter in the calendar_week construction to
lookup sessions_by_date.get(date, []) and use that list to populate
"has_session" and "sessions". Ensure you keep existing keys ("date",
"has_session", "is_today", "sessions") and formatting of start_time/end_time
when building the session dicts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/templates/courses/detail.html`:
- Around line 480-493: Add proper ARIA so screen-readers can discover the
sessions: give the tooltip container div (the one with classes "absolute
bottom-full left-1/2 transform -translate-x-1/2 mb-2 hidden group-hover:block
z-50") a unique id and role="tooltip" and ensure it has aria-hidden toggled with
its visible state; on the trigger span (the element that users hover/click to
open the tooltip) add aria-describedby pointing to that tooltip id (generate the
id using the template context like day or forloop index to keep it unique).
Ensure the id is stable and that aria-hidden is true when the tooltip is hidden
and false when visible so assistive tech receives the correct state.
- Around line 1538-1554: The tooltip HTML is vulnerable because s.title,
s.start_time and s.end_time are interpolated directly into tooltipHtml before
assigning to grid.innerHTML; add a small HTML-escape helper (e.g., escapeHtml)
at the top of the script and use it when building items inside the
day.sessions.map callback (escape s.title, s.start_time, s.end_time) so the
generated tooltipHtml contains escaped text rather than raw user-provided HTML.

In `@web/tests/test_views.py`:
- Around line 585-629: Ruff is raising PT009 for the idiomatic Django TestCase
assertion methods used in test_calendar_view and test_calendar_ajax_endpoint; do
not modify these assertions—instead update the Ruff configuration to ignore
PT009 for test files by adding "PT009" to the ignore list for the test path
pattern (e.g. "**/tests/**") so project-wide linting allows
assertIn/assertEqual/assertTrue in tests; ensure the change is applied in your
repository's ruff config (pyproject.toml or .ruff.toml) and not inside the test
files themselves.
- Around line 584-597: Replace the brittle truthiness check
self.assertTrue(len(day["sessions"]) > 0) with an explicit assertion to improve
failure messages: use self.assertGreater(len(day["sessions"]), 0) to assert the
sessions list is non-empty; additionally, after assigning session_info =
day["sessions"][0], add self.assertIsNotNone(session_info) to clearly assert the
extracted session exists before checking its keys (keep the existing asserts for
"title", "start_time", "end_time" and the equality against
self.future_session.title).
- Around line 599-629: In test_calendar_ajax_endpoint, strengthen assertions and
coverage: replace assertTrue(len(day["sessions"]) > 0) with
self.assertGreater(len(day["sessions"]), 0) and explicitly validate that
session_info["start_time"] and session_info["end_time"] are ISO 8601 strings
(e.g., parseable with datetime.fromisoformat or match
r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}'), and add two additional subtests that
call the same endpoint authenticated as a teacher and as a student (use the
existing test fixtures like self.teacher_user / self.student_user and
client.force_login or client.login) asserting status_code == 200 and that the
session appears in response.json()["calendar_weeks"] with correctly formatted
times.

In `@web/views.py`:
- Line 853: The out-of-month day cell appended in course_detail currently omits
the is_today key, causing schema mismatch with AJAX calendar payload; update the
calendar_week.append call in the course_detail view (the line creating {"date":
None, "in_month": False, "has_session": False, "sessions": []}) to include
"is_today": False so all day-cell dictionaries always have the same keys.

---

Outside diff comments:
In `@web/views.py`:
- Around line 3384-3397: The code repeatedly filters month_sessions for each day
cell (sessions_on_day) causing unnecessary rescans; instead, pre-group
month_sessions once into a dict keyed by date (as done in course_detail) — e.g.,
build a mapping like sessions_by_date = {s.start_time.date(): [s, ...] for s in
month_sessions} before the calendar loop, then replace the per-day filter in the
calendar_week construction to lookup sessions_by_date.get(date, []) and use that
list to populate "has_session" and "sessions". Ensure you keep existing keys
("date", "has_session", "is_today", "sessions") and formatting of
start_time/end_time when building the session dicts.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20cc175 and 7645e18.

📒 Files selected for processing (3)
  • web/templates/courses/detail.html
  • web/tests/test_views.py
  • web/views.py

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
web/tests/test_views.py (1)

599-629: 🧹 Nitpick | 🔵 Trivial

AJAX endpoint test looks solid; minor hardening opportunity.

The test correctly validates the JSON structure including calendar_weeks, current_month, and per-day session details. One observation:

The test doesn't assert anything about the start_time / end_time values — only their presence. If the serialization format changes (e.g., from "05:39 AM" to ISO 8601), this test would still pass silently. Consider adding a lightweight format or non-empty check.

♻️ Optional: assert times are non-empty strings
                     self.assertIn("start_time", session_info)
                     self.assertIn("end_time", session_info)
+                    self.assertTrue(session_info["start_time"], "start_time should be non-empty")
+                    self.assertTrue(session_info["end_time"], "end_time should be non-empty")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/tests/test_views.py` around lines 599 - 629, Update
test_calendar_ajax_endpoint to assert that the serialized times are meaningful:
after locating session_info (from data["calendar_weeks"] in
test_calendar_ajax_endpoint), add checks that session_info["start_time"] and
session_info["end_time"] are non-empty strings and optionally match a light
format (e.g., contain digits or a ":" or are valid ISO-like substrings) instead
of only asserting their presence; use the existing future_session reference to
compare or validate consistency if you prefer a stricter check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/templates/courses/detail.html`:
- Around line 1545-1576: The tooltip only appears on hover; update the trigger
span (the element built where class uses day.has_session and ariaAttr is
applied) to include tabindex="0" when day.has_session so it is
keyboard-focusable, and modify the tooltip container markup (where tooltipHtml
sets class="absolute ... hidden group-hover:block ...") to also include
group-focus-within:block so focus opens the tooltip; ensure these changes are
applied both in the JS-built tooltipHtml and the server-rendered template
referenced by ariaAttr/day.has_session.
- Around line 481-496: The tooltip currently uses a fixed "bottom-full" position
which can push it above the visible calendar for first-row dates; modify the
tooltip container (id "tooltip-{{ day.date|date:'Y-m-d' }}") to use a
conditional class that flips it when the date is in the first calendar row
(e.g., {% if day.is_first_row %}use "top-full mt-2" and adjust the arrow to
point up{% else %}keep "bottom-full" and current arrow{% endif %}). To do this
either add a boolean (is_first_row or week_index == 0) to the day objects in the
view context or compute it in the template, then apply the conditional class and
corresponding arrow markup adjustments inside the tooltip block so first-row
tooltips render below the date.

In `@web/views.py`:
- Around line 830-833: month_sessions is filtered from sessions which was
previously reordered (future-first), causing tooltip entries for the same date
to be out of chronological order; fix by ensuring month_sessions is ordered by
session start_time ascending (either filter from an original
chronologically-sorted source or sort month_sessions by the session.start_time
key) so tooltip entries appear in chronological order.

---

Duplicate comments:
In `@web/tests/test_views.py`:
- Around line 599-629: Update test_calendar_ajax_endpoint to assert that the
serialized times are meaningful: after locating session_info (from
data["calendar_weeks"] in test_calendar_ajax_endpoint), add checks that
session_info["start_time"] and session_info["end_time"] are non-empty strings
and optionally match a light format (e.g., contain digits or a ":" or are valid
ISO-like substrings) instead of only asserting their presence; use the existing
future_session reference to compare or validate consistency if you prefer a
stricter check.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7645e18 and 3ee2e62.

📒 Files selected for processing (3)
  • web/templates/courses/detail.html
  • web/tests/test_views.py
  • web/views.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

files-changed: 3 PR changes 3 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add session details tooltip on hover to course detail calendar

1 participant